﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NetfractionHub.Network.Protocol.BASE.Handler
{
	public class HandlerSUP : ProtocolMessageActionHandler
	{
		public HandlerSUP()
			: base()
		{
			ParameterCodes = new List<string>(new string[] {"AD", "RM"});

			mSupportedConnectionStates = new List<RemoteMachine.ConnectionState>();
			mSupportedConnectionStates.Add(RemoteMachine.ConnectionState.Protocol);
			mSupportedConnectionStates.Add(RemoteMachine.ConnectionState.Normal);
		}

		#region ProtocolMessageActionHandler Members

		public override string ProtocolActionCode
		{
			get { return "SUP"; }
		}

		public override bool HandleProtocolMessage(RemoteMachine.Node node, ProtocolMessage message, IEnumerable<ProtocolFeature> extenders)
		{
			RemoteMachine.Client cli = node as RemoteMachine.Client;
			if (!IsSupportedConnectionState(node))
			{
				ConnectionManager.DisconnectNode(cli);
				return false;
			}

			if (message.Parameters.Count == 0)
			{
				ConnectionManager.DisconnectNode(cli);
				return false;
			}

			message.Parameters = DecodeParameters(message.Parameters);

			var adders = from p in message.Parameters
						 where p.ParameterCode == "AD"
						 select p;

			var removers = from p in message.Parameters
						   where p.ParameterCode == "RM"
						   select p;

			foreach (ProtocolParameter param in adders)
			{
				// check if hub supports feature && feature isn't in client feature list already
				if (ConnectionManager.Hub.Features.ContainsKey(param.ParameterValue) && !node.Features.ContainsKey(param.ParameterValue))
					node.Features.Add(param.ParameterValue, ConnectionManager.Hub.Features[param.ParameterValue]);
			}
			
			foreach (ProtocolParameter param in removers)
			{
				// check if feature is in node feature list, then remove it
				if (node.Features.ContainsKey(param.ParameterValue))
					node.Features.Remove(param.ParameterValue);
			}

			// Not implemented yet. If the Feature behind the Session Hash Algorithm is removed by the client, we should disconnect them.
			/*if (removers.Count() > 0)
			{
				bool maintainConn = true;
				if (node.Features.Where(f => f.Value is IProtocolHashFeature).Count() > 0)
				{
					node.Features.Where(f => f.Value is IProtocolHashFeature).Where(p => (p.Value as IProtocolHashFeature).GetHashAlgorithm() == node.SessionHashAlgorithm);
					foreach (KeyValuePair<string, ProtocolFeature> feat in node.Features)
					{
						if (feat.Value is IProtocolHashFeature)
							if ((node.Features[feat.Key] as IProtocolHashFeature).GetHashAlgorithm() == node.SessionHashAlgorithm)
							{
								maintainConn = true;
								break;
							}
					}
				}
				else
				{
					maintainConn = false;
				}
			}*/

			if (node.Connection.State == NetfractionHub.RemoteMachine.ConnectionState.Protocol)
			{
				if (node.SessionHashAlgorithm == null)
				{
					foreach (KeyValuePair<string, ProtocolFeature> feat in node.Features)
					{
						if (feat.Value is IProtocolHashFeature)
						{
							node.SessionHashAlgorithm = (node.Features[feat.Key] as IProtocolHashFeature).GetHashAlgorithm();

							// only use the first algorithm detected
							break;
						}
					}

					if (node.SessionHashAlgorithm == null)
					{
						ConnectionManager.SendStatusMessageToNode(node, Factory.FactorySTA.StatusSeverity.FatalError, Factory.FactorySTA.StatusErrorCode.NoCommonHashFeatureWithHub);
						ConnectionManager.DisconnectNode(node);
						return false;
					}
				}

				ProtocolMessageFactory factory = new Factory.FactorySUP();
				Dictionary<int, object> factoryParams = new Dictionary<int, object>();

				factoryParams.Add((int)Factory.FactorySUP.ParameterEnum.AddFeature, node.Features.Keys.AsEnumerable());
				StringBuilder replyStr = new StringBuilder(factory.Create(ProtocolMessage.MessageTypeEnum.Info, factoryParams).ToString());
				replyStr.Append((char)0x0a);

				string clientSid = Network.ConnectionManager.GenerateNewSessionId(node);
				if (string.IsNullOrEmpty(clientSid))
				{
					ConnectionManager.SendStatusMessageToNode(node, Factory.FactorySTA.StatusSeverity.FatalError, Factory.FactorySTA.StatusErrorCode.GenericHubError);
					ConnectionManager.DisconnectNode(node);
					return false;
				}
				//factory = new Factory.FactorySID();
				//factoryParams = new Dictionary<int, object>();
				//factoryParams.Add((int)Factory.FactorySID.ParameterEnum.SessionId, clientSid);
				//replyStr.Append(factory.Create(ProtocolMessage.MessageTypeEnum.Info, factoryParams).ToString());
				replyStr.Append("ISID ");
				replyStr.Append(clientSid);
				replyStr.Append((char)0x0a);

				replyStr.Append(ConnectionManager.Hub.GetHubIdentityString());

				cli.Connection.Send(replyStr.ToString(), true);

				cli.Connection.State = NetfractionHub.RemoteMachine.ConnectionState.Identify;
			}
			else if (node.Connection.State == NetfractionHub.RemoteMachine.ConnectionState.Normal)
			{
				// probably need to send to other clients here
			}

			return true;
		}

		#endregion
	}
}
